#include "Run.cpp"
#include <fstream>
#include <sstream>
#include <vector>
#include <unistd.h>
#include <math.h>
char token[BUF_SIZE+3];
LL solutionId;
int isContest;
int judgeType;
int lang;
int problemId;
int contestId;
int timeLimit;
int timeLimitSpc;
int memoryLimit;
int isSpj;
int judgeFiles;
int dataVersion;
int maxMemory;
int maxTime;
int sumTime;
int passCases;
std::string detailCases;

uchar toHex(uchar c) {
    return c > 9 ? c + 55 : c + 48;
}
std::string urlEncode(char* str) {
    std::ostringstream osstream;
    char* p = str;
    while (*p != '\0') {
        if (isalnum((uchar)*p) || (*p == '-') || (*p == '_') || (*p == '.') || (*p == '~')) {
            osstream<<(*p);
        }
        else if (*p == ' ') {
            osstream<<'+';
        }
        else {
            osstream<<"%";
            osstream<<toHex((uchar)(*p) >> 4);
            osstream<<toHex((uchar)(*p) % 16);
        }
        ++p;
    }
    return osstream.str();
}

int createDirs(char* path) { //绝对路径
    char* p = path;
    std::string str = "";
    while (*p != '\0') {
        str += *p;
        ++p;
        while (*p != '/' && *p != '\0') {
            str += *p;
            ++p;
        }
        if (access(str.c_str(), F_OK) != 0) {
            if (mkdir(str.c_str(), 755) == -1) {
                return -1;
            }
        }
    }
    return 0;
}

bool isEq(std::vector<std::string>& v1, std::vector<std::string> &v2) {
    if (v1.size() != v2.size()) {
        return false;
    }
    int len = v1.size();
    for (int i=0; i<len; ++i) {
        if (v1[i] != v2[i]) {
            return false;
        }
    }
    return true;
}
int compareFile(char* user, char* dest) {
    std::ifstream uf(user);
    std::ifstream df(dest);
    int ret = OJ_OK;
    std::vector<std::string> vec1, vec2;
    std::string s;
    while (uf>>s) {
        vec1.push_back(s);
    }
    while (df>>s) {
        vec2.push_back(s);
    }
    if (!isEq(vec1, vec2)) {
        ret = OJ_WA;
    }
    if (ret == OJ_OK) {
        std::string line1, line2;
        uf.clear(); df.clear();
        uf.seekg(0, std::ios::beg);
        df.seekg(0, std::ios::beg);
        while(getline(df, line2, '\n')) {
            if(!getline(uf, line1, '\n')) {
                ret = OJ_PE;
                break;
            }
            int cur1=line1.size() - 1;
            int cur2=line2.size() - 1;
            while(cur1 > 0 && (line1[cur1] == ' ' || line1[cur1] == '\r')) --cur1;
            while(cur2 > 0 && (line2[cur2] == ' ' || line2[cur2] == '\r')) --cur2;
            if (cur1 != cur2) {
                ret = OJ_PE;
                break;
            }
            if (strncmp((char*)line1.data(), (char*)line2.data(), cur1) != 0) {
                ret = OJ_PE;
                break;
            }
        }
    }
    uf.close();
    df.close();
    return ret;
}

int myJudge(char* workdir, char* dataPath) {
    FILE* f =
    getCmdOutput("wget -qO- --post-data=\'name=%s&password=%s\' %s/auth/clientLogin",
            clientName, clientPassword, ojUrl);
    fgets(token, BUF_SIZE, f);
    fclose(f);
    if (isBlank(token)) {
        return 1;
    }
    f = getCmdOutput("wget -qO- --header=\'token: %s\' %s/code/getSolutionId",
            token, ojUrl);
    int ret = fscanf(f, "solutionId=%lld&isContest=%d", &solutionId, &isContest);
    fclose(f);
    if (ret != 2) {
        return 2;
    }
    if (debug) {
        printf("solutionId=%lld isContest=%d\n", solutionId, isContest);
    }
    f = getCmdOutput("wget -qO- --post-data=\'solutionId=%lld&isContest=%d\' --header=\'token: %s\' %s/code/getSolutionInfo",
            solutionId, isContest, token, ojUrl);
    ret = fscanf(f, "judgeType=%d&lang=%d&problemId=%d&contestId=%d", &judgeType, &lang, &problemId, &contestId);
    fclose(f);
    if (ret != 4) {
        return 3;
    }
    if (debug) {
        printf("judgeType=%d lang=%d problemId=%d contestId=%d\n", judgeType, lang, problemId, contestId);
    }
    ret = exeCmd("wget -qO- --post-data=\'solutionId=%lld&isContest=%d\' --header=\'token: %s\' %s/code/getCode > %s/Main.%s",
            solutionId, isContest, token, ojUrl, workdir, getCodeSuffix(lang).data());
    if (ret == -1) {
        return 4;
    }
    if (judgeType == customType) {
        double time, memory;
        ret = run(lang, workdir, customTimeLimit, customMemoryLimit, time, memory);
        maxTime = sumTime = time;
        maxMemory = memory;
        if (ret == 0) {
            passCases = 1;
        } else {
            passCases = 0;
        }
        detailCases += ('0' + ret);
        f = getCmdOutput("wget -qO- --post-data=\'solutionId=%lld&isContest=%d&maxMemory=%d&maxTime=%d&sumTime=%d&passCases=%d&detailCases=%s\' --header=\'token: %s\' %s/code/updateSolution",
                solutionId, isContest, maxMemory, maxTime, sumTime, passCases,
                detailCases.c_str(), token, ojUrl);
        fgets(buf, BUF_SIZE, f);
        fclose(f);
        if (strcmp(buf, "OK") != 0) {
            return 5;
        }
        std::string str = workdir;
        if (ret == OJ_CE) {
            str += "/user.err";
        } else {
            str += "/user.out";
        }
        std::ifstream in(str.data());
        std::ostringstream sstream;
        sstream<<in.rdbuf();
        f = getCmdOutput("wget -qO- --header=\'token: %s\' --header=\'solutionId: %lld\' --header=\'isContest: %d\' --post-data=\'%s\' %s/code/uploadOutput",
                token, solutionId, isContest, urlEncode((char*)sstream.str().data()).data(), ojUrl);
        fgets(buf, BUF_SIZE, f);
        fclose(f);
        if (strcmp(buf, "OK") != 0) {
            return 6;
        }
        exeCmd("rm -f %s/*", workdir);
    }
    else {
        f = getCmdOutput("wget -qO- --post-data=\'problemId=%d&contestId=%d\' --header=\'token: %s\' %s/code/getProblemInfo",
                problemId, contestId, token, ojUrl);
        ret = fscanf(f, "timeLimit=%d&timeLimitSpc=%d&memoryLimit=%d&isSpj=%d&judgeFiles=%d&dataVersion=%d",
                &timeLimit, &timeLimitSpc, &memoryLimit, &isSpj, &judgeFiles, &dataVersion);
        fclose(f);
        if (ret != 6) {
            return 7;
        }
        int localDataVersion = -1;
        std::ostringstream oss;
        oss<<dataPath<<"/"<<problemId;
        std::string problemPath = oss.str();
        oss<<"/dataVersion";
        std::string dataFilePath = oss.str();
        if (access(dataFilePath.data(), F_OK) == 0) {
            f = fopen(dataFilePath.data(), "r");
            fscanf(f, "%d", &localDataVersion);
            fclose(f);
        }
        if (localDataVersion < dataVersion) {
            if (access(problemPath.data(), F_OK) == 0) {
                exeCmd("rm -rf %s", problemPath.data());
            }
            createDirs((char*)problemPath.data());
            exeCmd("wget -qO- --post-data=\'problemId=%lld\' --header=\'token: %s\' %s/code/getProblemData > %s/data.zip",
                    problemId, token, ojUrl, problemPath.data());
            exeCmd("unzip -qo %s/data.zip -d %s", problemPath.data(), problemPath.data(), problemPath.data());
            exeCmd("rm -f %s/data.zip", problemPath.data());
            sprintf(buf, "%s/dataVersion", problemPath.data());
            f = fopen(buf, "w");
            fprintf(f, "%d", dataVersion);
            fclose(f);
        }
        if (isSpj) {
            exeCmd("cp %s/spj.cpp %s/", problemPath.data(), workdir);
        }
        detailCases = "";
        if (!compile(lang, workdir) || (isSpj && !compile(CPP_SPJ, workdir))) {
            maxTime = maxMemory = sumTime = 0;
            passCases = 0;
            detailCases += ('0' + OJ_CE);
            f = getCmdOutput("wget -qO- --post-data=\'solutionId=%lld&isContest=%d&maxMemory=%d&maxTime=%d&sumTime=%d&passCases=%d&detailCases=%s\' --header=\'token: %s\' %s/code/updateSolution",
                    solutionId, isContest, maxMemory, maxTime, sumTime, passCases,
                    detailCases.c_str(), token, ojUrl);
            fgets(buf, BUF_SIZE, f);
            fclose(f);
            if (strcmp(buf, "OK") != 0) {
                return 8;
            }
            std::string str = workdir;
            str += "/user.err";
            std::ifstream in(str.data());
            std::ostringstream sstream;
            sstream<<in.rdbuf();
            char* encodeOutput = (char*)urlEncode((char*)sstream.str().data()).data();
            if (strlen(encodeOutput) > BUF_SIZE - 1024) {
                return 8;
            }
            f = getCmdOutput("wget -qO- --header=\'token: %s\' --header=\'solutionId: %lld\' --header=\'isContest: %d\' --post-data=\'%s\' %s/code/uploadOutput",
                    token, solutionId, isContest, encodeOutput, ojUrl);
            fgets(buf, BUF_SIZE, f);
            fclose(f);
            if (strcmp(buf, "OK") != 0) {
                return 9;
            }
            exeCmd("rm -f %s/*", workdir);
            return 10;
        }
        passCases = 0;
        detailCases = "";
        maxTime = 0; maxMemory = 0;
        sumTime = 0;
        timeLimit = timeLimit*1000;
        if (lang == JAVA || lang == PYTH) {
            timeLimit = timeLimitSpc*1000;
        }
        for (int i=1; i<=judgeFiles; ++i) {
            oss.str("");
            oss<<problemPath;
            oss<<"/"<<i;
            std::string dataIn = oss.str() + ".in";
            std::string dataOut = oss.str() + ".out";
            exeCmd("cp %s %s/data.in", dataIn.data(), workdir);
            double time, memory;
            int timel;
            if (!(judgeType&2)) {//算总时间
                timel = timeLimit/1000 + 1;
            }
            else {
                timel = timeLimit/1000;
            }
            int state = OJ_OK;
            if (timel <= 0) {
                state = OJ_TL;
            }
            else {
                state = runSolution(lang, workdir, timel, memoryLimit, time, memory);
            }
            if (!(judgeType&2)) {//算总时间
                timeLimit -= time;
                if (state == OJ_OK && timeLimit <= 0) {
                    state = OJ_TL;
                }
            }
            if (maxTime < time) maxTime = time;
            if (lang == JAVA) {
                memory = std::max(memory-19.0, 1.0);
            }
            if (maxMemory < memory*1024) maxMemory = memory*1024;
            sumTime += time;
            if (state == OJ_OK) {
                if (!isSpj) { //非spj
                    std::string userOut = workdir;
                    userOut += "/user.out";
                    state = compareFile((char*)userOut.data(), (char*)dataOut.data());
                }
                else {
                    exeCmd("cp %s %s/data.out", dataOut.data(), workdir);
                    int ret = runSPJ(lang, 5, workdir);
                    if (ret == 0) {
                        state = OJ_OK;
                    }
                    else {
                        state = OJ_WA;
                    }
                }
            }
            if (state == OJ_OK) ++passCases;
            if (state == OJ_DC) {
                state = OJ_RE;
            }
            detailCases += ('0' + state);
            if (state != OJ_OK && !(judgeType&1)) { //错误优先
                break;
            }
        }
        exeCmd("rm -f %s/*", workdir);
        f = getCmdOutput("wget -qO- --post-data=\'solutionId=%lld&isContest=%d&maxMemory=%d&maxTime=%d&sumTime=%d&passCases=%d&detailCases=%s\' --header=\'token: %s\' %s/code/updateSolution",
                solutionId, isContest, maxMemory, maxTime, sumTime, passCases,
                detailCases.c_str(), token, ojUrl);
        fgets(buf, BUF_SIZE, f);
        fclose(f);
        if (strcmp(buf, "OK") != 0) {
            return 11;
        }
    }
    return 0;
}

int main(int argc, char** argv) {
//    printf("status: %d\n", myJudge((char*)"/home/judger/run3/", (char*)"/home/data"));
    int t = 1;
    while(1) {
        //myJudge((char*)"/home/judger/run3/", (char*)"/home/data")
        int status = myJudge((char*)"/home/judger/run3/", (char*)"/home/data");
        if (debug) printf("status: %d\n", status);
        if (status == 2 && t < 20) {
            ++t;
        }
        else {
            t = 1;
        }
        sleep(t);
    }
    return 0;
}
